package com.mapforge.collection;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

/**
 * The {@link WeakReferenceInstanceProvider} class provides an
 * {@link InstanceProvider} implementation that {@link #getInstance() gets
 * instances} by re-using a {@link WeakReference} if possible.
 * 
 * The {@link WeakReferenceInstanceProvider} class constructs instances via
 * {@link #createNewInstance()} defined in its subclass, and stores these new
 * instances into a {@link WeakReference} with the given possibly-null
 * {@link ReferenceQueue} to use to track them. When {@link #getInstance()} is
 * invoked, this {@link WeakReference} is checked, and if
 * {@link WeakReference#get()} returns non-null, this instance is returned,
 * otherwise a new instance is {@link #createNewInstance() created} and returned
 * after being stored into a new {@link WeakReference} with the configured
 * {@link ReferenceQueue}.
 * 
 * @param <T>
 *            The supported parameterized {@link Object} type of this
 *            {@link WeakReferenceInstanceProvider} instance.
 */
public abstract class WeakReferenceInstanceProvider<T> implements
		InstanceProvider<T> {

	/**
	 * The possibly-null {@link ReferenceQueue} to use to track
	 * {@link #getInstance() instances}.
	 * 
	 * @see WeakReference#WeakReference(Object, ReferenceQueue)
	 * @see #getInstance()
	 */
	private ReferenceQueue<T> queue;

	/**
	 * The possibly-null current {@link WeakReference} which tracks a new
	 * {@link #getInstance() instance}.
	 */
	private WeakReference<T> currentReference;

	/**
	 * Creates a new {@link WeakReferenceInstanceProvider} instance with a null
	 * {@link ReferenceQueue} to use to track {@link #getInstance() instances}.
	 * 
	 */
	public WeakReferenceInstanceProvider() {
		this(null);
	}

	/**
	 * Creates a new {@link WeakReferenceInstanceProvider} instance with the
	 * possibly-null {@link ReferenceQueue} to use to track
	 * {@link #getInstance() instances}.
	 * 
	 * @param queue
	 *            The possibly-null {@link ReferenceQueue} to use to track
	 *            {@link #getInstance() instances}.
	 */
	public WeakReferenceInstanceProvider(final ReferenceQueue<T> queue) {
		super();
		this.queue = queue;
	}

	public T getInstance() {
		if (currentReference == null) {
			return createWeakReferenceAndReturnInstance();
		} else {
			T currentInstance = currentReference.get();
			return currentInstance == null ? createWeakReferenceAndReturnInstance()
					: currentInstance;
		}
	}

	/**
	 * {@link #createNewInstance() Creates} a new parameterized {@link Object}
	 * and stores it into a new {@link WeakReference} instance with the
	 * configured {@link ReferenceQueue} to be used in later invocations of
	 * {@link #getInstance()}, and then returns the new paramaterized
	 * {@link Object} instance.
	 * 
	 * @return A newly {@link #createNewInstance() created instance}.
	 */
	private T createWeakReferenceAndReturnInstance() {
		T newInstance = createNewInstance();
		currentReference = new WeakReference<T>(newInstance, queue);
		return newInstance;
	}

	/**
	 * Creates a new non-null instance of the supported parameterized
	 * {@link Object} class.
	 * 
	 * @return A new non-null instance of the supported parameterized
	 *         {@link Object} class.
	 */
	protected abstract T createNewInstance();

}
